#include "kernel.h"
#include "globals.h"
#include "types.h"
#include "errno.h"

#include "util/string.h"
#include "util/printf.h"
#include "util/debug.h"

#include "fs/dirent.h"
#include "fs/fcntl.h"
#include "fs/stat.h"
#include "fs/vfs.h"
#include "fs/vnode.h"

/* This takes a base 'dir', a 'name', its 'len', and a result vnode.
 * Most of the work should be done by the vnode's implementation
 * specific lookup() function, but you may want to special case
 * "." and/or ".." here depnding on your implementation.
 *
 * If dir has no lookup(), return -ENOTDIR.
 *
 * Note: returns with the vnode refcount on *result incremented.
 */
int
lookup(vnode_t *dir, const char *name, size_t len, vnode_t **result)
{
        KASSERT(NULL != dir);
	if(!S_ISDIR(dir->vn_mode))
		return -ENOTDIR;
	
	if(strlen(name)==0)
		return -EINVAL;
	
	int x;	
	/*if((strlen(name)==0))
	{
	x=dir->vn_ops->lookup(dir,".",1,result);
	}
	else
	{*/
	dbgq(DBG_SCHED,"Lookup 1\n");
	x = dir->vn_ops->lookup(dir,name,len,result); 
	dbgq(DBG_SCHED,"Lookup 2\n");
	/*} */
	
	KASSERT(NULL != name);
        KASSERT(NULL != result);

	return x;
	
        
}


/* When successful this function returns data in the following "out"-arguments:
 *  o res_vnode: the vnode of the parent directory of "name"
 *  o name: the `basename' (the element of the pathname)
 *  o namelen: the length of the basename
 *
 * For example: dir_namev("/s5fs/bin/ls", &namelen, &name, NULL,
 * &res_vnode) would put 2 in namelen, "ls" in name, and a pointer to the
 * vnode corresponding to "/s5fs/bin" in res_vnode.
 *
 * The "base" argument defines where we start resolving the path from:
 * A base value of NULL means to use the process's current working directory,
 * curproc->p_cwd.  If pathname[0] == '/', ignore base and start with
 * vfs_root_vn.  dir_namev() should call lookup() to take care of resolving each
 * piece of the pathname.
 *
 * Note: A successful call to this causes vnode refcount on *res_vnode to
 * be incremented.
 */
int
dir_namev(const char *pathname, size_t *namelen, const char **name,
          vnode_t *base, vnode_t **res_vnode)
{
	unsigned int i=0;
	KASSERT(NULL != pathname);
	if(strlen(pathname)>MAXPATHLEN)
		return -ENAMETOOLONG;
	
	vnode_t *temp;
	vnode_t *temp1;
	vnode_t *result;
	temp=base;
	temp1=base;

	char teststr[MAXPATHLEN];
	strcpy(teststr, pathname);
		
	if(base==NULL)
	{
			
		temp=curproc->p_cwd;
		temp1=curproc->p_cwd;
	}
	
	char path[MAXPATHLEN];
	strcpy(path,pathname);
	char pp[MAXPATHLEN];
	if(pathname[0] == '/') 
	{	
		for (i=0;i<strlen(pathname);i++)
			pp[i]=path[i+1];     
		pp[i]='\0';	
	
		temp=vfs_root_vn;
		temp1=vfs_root_vn;
	}
	else
	{
		for (i=0;i<strlen(pathname);i++)
			pp[i]=path[i];     
		pp[i]='\0';
	}
	
	char * pch1;
	int k=0; int j=0;
	pch1=strchr(pp,'/');
	char * pch;
  	pch = strtok (teststr,"/");
	if(pch1 == NULL)
	{
		*name=pp;
		*namelen=strlen(pp);
		*res_vnode=temp;
		vref(*res_vnode);	    
		
		return 0;	
	}
	if(pch == NULL)
	{
		*name=pathname;
		*namelen=strlen(pathname);
		*res_vnode=temp;
		vref(*res_vnode);	    
		return 0;	
	}
	
	char pd[MAXPATHLEN];
	char teststr1[MAXPATHLEN];
	strcpy(teststr1, pathname);
	char * pchcpy;
	pchcpy = strtok (teststr1,"/");
	
	while(pchcpy!=NULL)
	{
		
		pd[j]='/';
		j++;		
		for(i=0;i<strlen(pchcpy);i++)
		{
			pd[j]=pchcpy[i];
			j++;
		}
		
		pchcpy=strtok(NULL,"/");
	}

		
	pd[j]='\0';

        for(k=j;k>=0;k--)
        {
        	if(pd[k]=='/')
        	{
			pd[k]='\0';
        		break;
		}  
        }
	
	char * pch2;
	pch2 = strtok (pd,"/");
	dbgq(DBG_SCHED,"DIR_NAMEV 1\n");
	KASSERT(NULL != temp); 
 	while (pch2 != NULL)
        {
	dbgq(DBG_SCHED,"DIR_NAMEV 2\n");
		        int x=lookup(temp,pch2,strlen(pch2), &result);
	      			dbgq(DBG_SCHED,"DIR_NAMEV 3\n");
			
			*name=pch2;
			*namelen=strlen(pch2);
			*res_vnode=temp;
			temp=result;
			if(x>=0)
			{
								
				if(S_ISREG(result->vn_mode))
				{
									
				/*vput(result);*/
					return -ENOTDIR;
				}
			}
			if(x==-ENOTDIR || x==-ENOENT)
			{
			*name=NULL;
			*namelen=NULL;
			*res_vnode=NULL;
			
			return x;
			}
			if(x==0)
			 { /*vput(result);*/     }
			pch2 = strtok (NULL, "/");
						
        }

	vnode_t *result1;
	char teststr2[MAXPATHLEN];
	strcpy(teststr2, pathname);
	char * pch3;
	pch3 = strtok (teststr2,"/");
	int l=0;
	while (pch3 != NULL)
        {
				dbgq(DBG_SCHED,"DIR_NAMEV 4\n");
			l=lookup(temp1,pch,strlen(pch3), &result1);
				dbgq(DBG_SCHED,"DIR_NAMEV 5\n");
			*name=pch3;
			*namelen=strlen(pch3);
			*res_vnode=temp;
			temp1=result1;
			if(l==0)
			{			
			  /*vput(result1);*/    
			}
			pch3= strtok (NULL, "/");							
        }

	vref(*res_vnode); 	     
	
	
	KASSERT(NULL != namelen);
	KASSERT(NULL != name);
	
	KASSERT(NULL != res_vnode);
        return 0;
}

/* This returns in res_vnode the vnode requested by the other parameters.
 * It makes use of dir_namev and lookup to find the specified vnode (if it
 * exists).  flag is right out of the parameters to open(2); see
 * <weenix/fnctl.h>.  If the O_CREAT flag is specified, and the file does
 * not exist call create() in the parent directory vnode.
 *
 * Note: Increments vnode refcount on *res_vnode.
 */
int
open_namev(const char *pathname, int flag, vnode_t **res_vnode, vnode_t *base)
{
	
dbgq(DBG_SCHED,"OPEN_NAMEV 1\n");
	char teststr[MAXPATHLEN];
	strcpy(teststr, pathname);
	char * pch;
  	pch = strtok (teststr,"/");
	char pd[MAXPATHLEN];
	unsigned int i=0;
	int j=0;  
	int k=0;      
	while(pch!=NULL)
	{
		pd[j]='/';
		j++;		
		for(i=0;i<strlen(pch);i++)
		{
			pd[j]=pch[i];
			j++;
		}
		pch=strtok(NULL,"/");
	}
dbgq(DBG_SCHED,"OPEN_NAMEV 2\n");
		
	pd[j]='\0';
        for(k=j;k>=0;k--)
        {
        	if(pd[k]=='/')
        	{
			pd[k]='\0';
        		break;
		}  
        }
	
	size_t namelen=0;
	const char *name=NULL;
	vnode_t *res_vnode1=NULL;
	vnode_t *result;
dbgq(DBG_SCHED,"OPEN_NAMEV 3\n");
	int x=dir_namev(pd, &namelen, &name,base, &res_vnode1);
dbgq(DBG_SCHED,"OPEN_NAMEV 4\n");
	if(x>=0)
	 { /*vput(res_vnode1);*/    }
	if(x<0)
		return x;
dbgq(DBG_SCHED,"OPEN_NAMEV 5\n");
	int y=dir_namev(pathname, &namelen, &name,base, &result);
dbgq(DBG_SCHED,"OPEN_NAMEV 6\n");
	if(name==NULL && result==NULL)
		return -ENOENT;	
	
	if(y>=0)
	{
		/*vput(result);*/    
		if(!S_ISDIR(result->vn_mode))
		return -ENOTDIR;	
	}
	if(y<0)
		return y;
dbgq(DBG_SCHED,"OPEN_NAMEV 7\n");
	int z=lookup(result, name, strlen(name),res_vnode);
dbgq(DBG_SCHED,"OPEN_NAMEV 8\n");
		
	if(z>=0)
	{
		return 0;
	}
	
	else if ((flag&O_CREAT)==O_CREAT)
	{ 
		dbgq(DBG_PROC,"CREATING FILE IN OPENV . NAME IS %s\n",name);	 
		if(!S_ISDIR(result->vn_mode))
			return -ENOTDIR; 
        	KASSERT(NULL != result->vn_ops->create);
		int z=result->vn_ops->create(result,name,namelen,res_vnode);
		if(z<0)
			return z;
		 dbgq(DBG_PROC,"CREATED FILE IN OPENV \n");
				
		vref(*res_vnode);	    
        	return 0;	
	}
	else if (z<0)
	{
		return z;
	}
	return 0;
}

#ifdef __GETCWD__
/* Finds the name of 'entry' in the directory 'dir'. The name is writen
 * to the given buffer. On success 0 is returned. If 'dir' does not
 * contain 'entry' then -ENOENT is returned. If the given buffer cannot
 * hold the result then it is filled with as many characters as possible
 * and a null terminator, -ERANGE is returned.
 *
 * Files can be uniquely identified within a file system by their
 * inode numbers. */
int
lookup_name(vnode_t *dir, vnode_t *entry, char *buf, size_t size)
{
        NOT_YET_IMPLEMENTED("GETCWD: lookup_name");
        return -ENOENT;
}


/* Used to find the absolute path of the directory 'dir'. Since
 * directories cannot have more than one link there is always
 * a unique solution. The path is writen to the given buffer.
 * On success 0 is returned. On error this function returns a
 * negative error code. See the man page for getcwd(3) for
 * possible errors. Even if an error code is returned the buffer
 * will be filled with a valid string which has some partial
 * information about the wanted path. */
ssize_t
lookup_dirpath(vnode_t *dir, char *buf, size_t osize)
{
        NOT_YET_IMPLEMENTED("GETCWD: lookup_dirpath");

        return -ENOENT;
}
#endif /* __GETCWD__ */
